/***************************************************************************************
 * Copyright (c) 2014-2022 Zihao Yu, Nanjing University
 *
 * NEMU is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 *PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *
 * See the Mulan PSL v2 for more details.
 ***************************************************************************************/

#include "sdb.h"

#define NR_WP 32

static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i++) {
    wp_pool[i].NO = i;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
    wp_pool[i].expr[0] = '\0';
    wp_pool[i].hit_times = 0;
  }

  head = NULL;
  free_ = wp_pool;
}

/* TODO: Implement the functionality of watchpoint */
WP *new_wp() {
  if (free_ == NULL) {
    printf("No more watchpoint!\n");
    assert(0);
  }
  // get one watchpoint from free_ and reset head pointer
  WP *p = free_;
  free_ = free_->next;
  p->next = head;
  head = p;
  return p;
}

void free_wp(int n) {
  WP *wp = NULL;
  if (head->NO == n) {
    wp = head;
    head = head->next;
    wp->next = free_;
    free_ = wp;
    return;
  } else {
    WP *pre = head;
    while (pre->next != NULL) {
      if (pre->next->NO == n) {
        wp = pre->next;
        pre->next = pre->next->next;
        wp->next = free_;
        free_ = wp;
        return;
      }
      pre = pre->next;
    }
  }
  if (wp == NULL) {
    printf("No such watchpoint!\n");
  }
  assert(0);
}

void display_wp() {
  printf("Num\tExpr\t\tHit times\n");
  WP *p = head;
  while (p != NULL) {
    printf("%d\t%s\t\t%d\n", p->NO, p->expr, p->hit_times);
    p = p->next;
  }
}

void check_wp() {
  bool success = true;
  WP *p = head;
  while (p != NULL) {
    uint32_t new_value = expr(p->expr, &success);
    if (success == false) {
      printf("Invalid expression!\n");
      assert(0);
    }
    if (new_value != p->old_value) {
      printf("Watchpoint %d: %s\n", p->NO, p->expr);
      printf("Old value = 0x%x\n", p->old_value);
      printf("New value = 0x%x\n", new_value);
      p->old_value = new_value;
    }
    p = p->next;
  }
}
